#include <ncurses.h>
#include <fstream>
#include "../backend/board.hpp"
#include "../backend/game.hpp"
#include "../backend/player_vs_player.hpp"
#include "../backend/melee.hpp"
#include "res.hpp"

const int board_size=15;

void update_point(gomoku::point pos, gomoku::point_status status){ // 更新视图中棋盘上的一个方格
    switch (status){
        case gomoku::point_status::vacant:
            mvprintw(pos.row+1,2*pos.col,"- ");
            break;
        case gomoku::point_status::disabled:
            mvprintw(pos.row+1,2*pos.col,"x ");
            break;
        case gomoku::point_status::black:
            mvprintw(pos.row+1,2*pos.col,"# ");
            break;
        case gomoku::point_status::white:
            mvprintw(pos.row+1,2*pos.col,"o ");
            break;
    }
}

void update_view(gomoku::board &b){ // 根据棋盘变化自动更新视图
    if (!b.change_log.empty()){
        auto change = b.change_log.back();
        update_point(change.pos,change.event);
    }
}

void update_line(int line, const char *s){ // 在指定的一行显示指定内容
    int i;
    for (i=0;i<COLS;++i){
        mvprintw(line,i," ");
    }
    mvprintw(line,0,s);
}

bool save_game(char *filename, gomoku::game &game){ // 保存游戏,返回保存是否成功
    try{
        std::ofstream outfile;
        outfile.open(filename,std::ios::out|std::ios::binary);
        game.save(outfile);
        outfile.close();
    }catch (...){
        return false;
    }
    return true;
}

void start_game(gomoku::game &game){
    gomoku::point cursor_position{game.board.height()/2+1,game.board.width()/2+1};
    gomoku::player winner;
    // 设定在棋盘发生变化时自动更新棋盘
    game.board.on_event = update_view;

    //绘制初始屏幕
    clear();
    for (int i=0;i<game.board.height();++i){
        for (int j=0;j<game.board.width();++j){
            update_point({i,j},game.board[{i,j}]);
        }
    }

    // 当棋盘没有占满也没有任何一方胜利时，游戏进行
    while (true){
        // 更新屏幕
        // 显示游戏状态
        update_line(0,prompt[game.game_mode()][game.status()]);
        move(cursor_position.row+1,2*cursor_position.col);
        refresh();
        // 如果游戏结束,i.e.一方胜利或棋盘已满，退出循环
        if (game.board.vacant_count()==0 || (winner=game.winner())!=gomoku::player::nobody){
            break;
        }
        // 读取玩家操作并执行对应动作
        char key;
        // 移动光标
        while ((key=getc(stdin))!=' '){
            // 处理方向键
            if (key=='\033'){
                key = getc(stdin);
                key = getc(stdin);
                switch (key){
                    case 'A':
                        key = 'W';
                        break;
                    case 'B':
                        key = 'S';
                        break;
                    case 'C':
                        key = 'D';
                        break;
                    case 'D':
                        key = 'A';
                        break;
                }
            }
            switch (key){
                case 'W':
                case 'w':
                case 'K':
                case 'k':
                    --cursor_position.row;
                    break;
                case 'A':
                case 'a':
                case 'H':
                case 'h':
                    --cursor_position.col;
                    break;
                case 'S':
                case 's':
                case 'J':
                case 'j':
                    ++cursor_position.row;
                    break;
                case 'D':
                case 'd':
                case 'L':
                case 'l':
                    ++cursor_position.col;
                    break;
                case 'Q':
                case 'q':
                    // 退出游戏
                    clear();
                    refresh();
                    return;
                    break;
                case 'E':
                case 'e':
                    // 保存游戏
                    char filename[512];
                    // 提示用户输入文件名
                    clear();
                    mvprintw(0,0,"请输入要写入的游戏文件名");
                    // 开启回显
                    echo();
                    // 读取输入
                    mvgetstr(1,0,filename);
                    // 保存游戏
                    clear();
                    if (save_game(filename,game)){
                        mvprintw(0,0,"保存成功,按任意键返回");
                    }else{
                        mvprintw(0,0,"保存失败,按任意键返回");
                    }
                    // 结束游戏
                    refresh();
                    noecho();
                    getc(stdin);
                    return;
                    break;
                case 'U':
                case 'u':
                    // 悔棋
                    if (game.undo_operation()){
                        update_line(0,"悔棋成功，请重新走棋");
                    }else{
                        update_line(0,"当前无法悔棋");
                    }
                    break;
            }
            // 处理光标越界
            cursor_position.row+=game.board.height();
            cursor_position.col+=game.board.width();
            cursor_position.row%=game.board.height();
            cursor_position.col%=game.board.width();
            // 刷新光标位置
            move(cursor_position.row+1,2*cursor_position.col);
            refresh();
        }
        // 按下空格后，落子，并开始新一轮循环以更新棋盘
        game.do_operation(cursor_position);
    }

    // 显示胜利者
    switch(winner){
        case gomoku::player::nobody:
            update_line(0,"平局，按任意键退出");
            break;
        case gomoku::player::black:
            update_line(0,"黑方胜利，按任意键退出");
            break;
        case gomoku::player::white:
            update_line(0,"白方胜利，按任意键退出");
            break;
    }
    refresh();
    getc(stdin);
}

int main(){
    // 初始化本地化设定以便正确显示画面
    setlocale(LC_ALL, "");
    // 初始化ncurses设定
    initscr();
    cbreak();
    noecho();

    // 显示主菜单
    bool in_menu = true; // 表示用户是否会返回主菜单
    while (in_menu){
        clear();
        mvprintw(0,0,
            "欢迎来到五子棋，请按数字键选择模式\n"
            "1 普通模式\n"
            "2 混战模式\n"
            "3 查看帮助\n"
            "4 自定义游戏\n"
            "5 读取保存的游戏\n"
            "6 退出游戏\n"
        );
        refresh();
        char key = getc(stdin);
        if (key=='1'){
            gomoku::player_vs_player game{board_size,board_size};
            start_game(game);
        }else if (key=='2'){
            gomoku::melee game{board_size,board_size};
            start_game(game);
        }else if (key=='3'){
            // 显示帮助信息
            clear();
            mvprintw(0,0,helpstr);
            refresh();
            getc(stdin);
        }else if (key=='4'){
            // 提示用户输入游戏信息
            int height,width,mode;
            clear();
            mvprintw(0,0,custom_game_prompt);
            // 开启输入回显
            echo();
            // 读取输入
            mvscanw(1,0,"%d",&height);
            mvscanw(3,0,"%d",&width);
            mvscanw(5,0,"%d",&mode);
            // 关闭输入回显
            noecho();

            // 启动游戏
            if (mode==1){
                gomoku::player_vs_player game{height,width};
                start_game(game);
            }else if (mode==2){
                gomoku::melee game{height,width};
                start_game(game);
            }
        }else if (key=='5'){
            try{
                int height,width,size,mode,status;
                gomoku::point_status *layout;
                char filename[512];
                // 提示用户输入文件名
                clear();
                mvprintw(0,0,"请输入要读取的游戏文件名");
                // 开启回显
                echo();
                // 读取输入
                mvgetstr(1,0,filename);
                // 载入游戏
                std::ifstream infile;
                infile.open(filename,std::ios::in|std::ios::binary);
                infile.read((char*)&mode,sizeof(int));
                infile.read((char*)&status,sizeof(int));
                infile.read((char*)&height,sizeof(int));
                infile.read((char*)&width,sizeof(int));
                size = height*width;
                layout = new gomoku::point_status[size];
                infile.read((char*)layout,size*sizeof(int));
                if (mode==1){
                    gomoku::player_vs_player game{height,width,status,layout};
                    start_game(game);
                }else if (mode==2){
                    gomoku::melee game{height,width,status,layout};
                    start_game(game);
                } 
                delete [] layout;
            }catch(...){
                clear();
                mvprintw(0,0,"读取失败,按任意键返回");
                refresh(); 
                getc(stdin);
            }
        }else if (key=='6'){
            in_menu = false;
        }
    }

    // 终止ncurses
    endwin();
    return 0;
}
